﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace QuailDashboard.View.Helpers
{
    public static class MouseWheel
    {
        public const double DefaultScrollingSpeed = 0.5;

        private static double _scrollingSpeed = 0.5;

        /// <summary>
        /// Attaches MouseWheel vertical scrolling to the target ScrollViewer.  Uses the value of DefaultScrollingSpeed to set the speed.
        /// </summary>
        /// <param name="viewer">The target ScrollViewer</param>
        public static void AttachMouseScrolling(this ScrollViewer viewer)
        {
            AttachMouseScrolling(viewer, DefaultScrollingSpeed);
        }

        /// <summary>
        /// Attaches MouseWheel vertical scrolling to the target ScrollViewer.
        /// </summary>
        /// <param name="viewer">The target ScrollViewer</param>
        /// <param name="speed">The speed for scrolling</param>
        public static void AttachMouseScrolling(this ScrollViewer viewer, double speed)
        {
            _scrollingSpeed = speed;
            if (viewer.Tag == null)
            {
                viewer.Tag = speed;
            }

            viewer.MouseWheel += new MouseWheelEventHandler(ViewerMouseWheel);

            HitTestFix(viewer);
        }

        /// <summary>
        /// Attaches MouseWheel vertical scrolling to the target UIElement.
        /// </summary>
        /// <param name="element">The target UIElement</param>
        public static void AttachMouseScrolling(this UIElement element)
        {
            element.MouseWheel += new MouseWheelEventHandler(ElementMouseWheel);
            
            HitTestFix(element);
        }

        /// <summary>
        /// Silverlight 3 will not fire MouseWheel events if the mouse is over a control with a BackGround with no brush.
        /// A brush is added in this case to get the excepted results for the MouseWheel event.
        /// </summary>
        /// <param name="element">The element to "fix"</param>
        private static void HitTestFix(UIElement element)
        {
            var control = element as Control;
            if (control != null && control.Background == null)
            {
                control.Background = new SolidColorBrush();
            }
        }

        /// <summary>
        /// EventHandler for MouseWheel events attached to ScrollViewer objects
        /// </summary>
        /// <param name="sender">The object sending the event</param>
        /// <param name="e">The event args</param>
        private static void ViewerMouseWheel(object sender, MouseWheelEventArgs e)
        {
            var viewer = sender as ScrollViewer;
            if (viewer == null) return;
            double speed = (viewer.Tag != null && viewer.Tag is double) ? (double)viewer.Tag : _scrollingSpeed;
            double offset = viewer.VerticalOffset - (e.Delta * _scrollingSpeed);
            viewer.ScrollToVerticalOffset(offset);
        }

        /// <summary>
        /// EventHandler for MouseWheel events attached to UIElement objects
        /// </summary>
        /// <param name="sender">The object sending the event</param>
        /// <param name="e">The event args</param>
        private static void ElementMouseWheel(object sender, MouseWheelEventArgs e)
        {
            var element = sender as UIElement;
            if (sender == null) return;

            var automationPeer = FrameworkElementAutomationPeer.FromElement(element) 
                ?? FrameworkElementAutomationPeer.CreatePeerForElement(element);
            var scrollProvider = automationPeer.GetPattern(PatternInterface.Scroll) as IScrollProvider;

            if (scrollProvider != null && scrollProvider.VerticallyScrollable)
            {
                var verticalScrollAmount = DeltaToScrollAmount(e.Delta);
                System.Diagnostics.Debug.WriteLine("MouseWheel Delta = {0}; ScrollAmount = {1}", e.Delta, verticalScrollAmount);
                scrollProvider.Scroll(ScrollAmount.NoAmount, verticalScrollAmount);
            }
        }

        /// <summary>
        /// Converts MouseWheel event delta value to a ScrollAmount.
        /// </summary>
        /// <param name="delta">The delta value</param>
        /// <returns>The resulting ScrollAmount</returns>
        private static ScrollAmount DeltaToScrollAmount(int delta)
        {
            if (delta == 0) return ScrollAmount.NoAmount;
            if (delta > 240) return ScrollAmount.LargeDecrement;
            if (delta < -240) return ScrollAmount.LargeIncrement;
            if (delta > 0) return ScrollAmount.SmallDecrement;
            return ScrollAmount.SmallIncrement;
        }
    }
}
